package main

import (
	"errors"
	"fmt"
	"golang.org/x/sync/errgroup"
	"sync"
)

//初级程序员
//学会了使用函数
func Factorial(n int) int {
	res := 1
	for i := 1; i <= n; i++ {
		res *= n
	}
	return res
}

//学会了使用函数的程序员
func FactorialWithFun(n int) int {
	if n == 0 {
		return 1
	} else {
		return FactorialWithFun(n-1) * n
	}
}

//所有的初始化的函数 + * 计算 都可以进行使用 地柜的函数去
// f(4)*4 f(4)= f(3)*3
// f(4)=>1*2*3*4
// n
// if n ==0 retunr 1
// f(n-1)*n
//可以按照这种的线性代数和数据本身的规律和数学思维和发现规律的思维去处理
//当发现了数字+数学+统计+计算+结果+算法+x,z,y,kz + 构造
//学会了思考的程序员
//学会如何在 Go中使用泛型的程序员
//单独-思考-考察-实际-通用-泛型-多线程-协助-合作-通信-解耦合-外部-内部
//
func FactorialWithInterface(n interface{}) interface{} {
	v, valid := n.(int)
	if !valid {
		return 0
	}
	if v == 0 {
		return 1
	}
	return FactorialWithInterface(v-1).(int) * v
}

//多线程优化过的Go程序员
func FactorialWthMutiSync(n int) int {
	var (
		left, right = 1, 1
		wg          sync.WaitGroup
	)
	wg.Add(2)
	pivot := n / 2
	go func() {
		for i := 1; i < pivot; i++ {
			left *= i
		}
		wg.Done()
	}()
	go func() {
		for i := pivot; i < n; i++ {
			right *= i
		}
		wg.Done()
	}()
	wg.Wait()
	return left * right
}

//学会了设计模式的Go程序员
func FactorialWithDesign(n int) <-chan int {
	ch := make(chan int)
	go func() {
		prev := 1
		for i := 1; i < n; i++ {
			v := prev * i
			ch <- v
			prev = v
		}
		close(ch)
	}()
	return ch
}

//使用了 wg.add
// go func wg.wait
// 将结果保存
//返回c
//堵塞
//然后进行wg.wait
// go go
// go wg.wait
// c
// fmit
// close
func FactoryWithInfoGroup(n int) (<-chan int, <-chan error) {
	c := make(chan int)
	e := make(chan error)
	var (
		left, right = 1, 1
		er          error
		group       = new(errgroup.Group)
	)
	pivot := n / 2
	group.Go(func() error {
		for i := 1; i < pivot; i++ {
			left *= i
		}
		return nil
	})
	group.Go(func() error {
		for i := pivot; i < n; i++ {
			right *= i
		}
		return errors.New("sin")
	})
	go func() {
		defer close(c)
		er = group.Wait()
		go func() {
			defer close(e)
			e <- er
		}()
		c <- left * right
	}()
	return c, e
}
func FactoryWithInfo(n int) (<-chan int, error) {
	c := make(chan int)
	var (
		left, right = 1, 1
		wg          sync.WaitGroup
		er          error
	)
	pivot := n / 2
	wg.Add(2)
	go func() {
		for i := 1; i < pivot; i++ {
			left *= i
		}
		wg.Done()
	}()
	go func() {
		for i := pivot; i < n; i++ {
			right *= i
		}
		wg.Done()
	}()
	go func() {
		defer close(c)    //完成关闭通道
		wg.Wait()         //堵塞等待 上面完成处理
		c <- left * right //如果没有消费则堵塞在这里
	}()
	return c, er
}

//但是上面还是有问题协程的处理 异常怎么返回 在日志？程序？业务？中间件？
// go 团队在实验仓库中添加了一个名为sync.errgroup 新软件包 sync.ErrGroup 在 sync.waitGroup 基础之上
// 增加了错误传递 以及发生不可恢复的错误时取消整个goroutine 集合或者等待超时
//所以所以的在写gorutine 注意思索 等待 延迟时间监控 上下文错误信息传递 取消
//组合和有序和无序处理
// WithContext ctx context.Context Group context.context
// Go() 方法 不仅允许 多核 调度 runtime gmp 本地 全局 所有信号 桑希望 gc
// 而且还能捕获错误信息 你只要像这样返回 一个错误 return err 尤其在rpc中 grpc pod pod
//必须返回 args reply error args replu error
// handle
// go func
// stack
// dui
// 这样是开放这使用 goroutines 时开发效率明显提高
// 用sync glamng.org/x/sync/errgorup
//func(g *Group) Go(func()error)
// func(g *Group)Wait() error
func Group() {
	group := new(errgroup.Group)
	nums := []int{-1, 0, 1}
	for _, num := range nums {
		tempNum := num //子携程直接访问 num 需要做对应的句柄编号拷贝 需要使用零食变量
		//在修改对应的nums 是值拷贝 需要修改 则需要使用 nums[key]这样的方式修改
		// num 无法修改会修改最后一个变量的地址空间
		//子协程
		group.Go(func() error {
			if tempNum < 0 {
				return errors.New("tempNum <0 !!!")
			}
			fmt.Println("tempNum:", tempNum)
			return nil
		})
	}
	//捕获err
	if err := group.Wait(); err != nil {
		fmt.Println("GetErrors: ", err)
	} else {
		fmt.Println("Get all num successfully")
	}
}

//在设计的时候 如果当前的协程也是从本地队列-全局队列-本地会导致思索的文艺
//资源通用时间不可分割 不可复制占多的善斌
//关闭的思索信息
//学会使用成熟的方案弥补Go的不足
type IFactorial interface {
	CalculateFactorial() int
}

//factorialIml implemetys Ifactioral
var _ IFactorial = (*FactorialImpl)(nil)

// Used tp find factoal of the n.
type FactorialImpl struct {
	n int
}

func NewFactorial(n int) *FactorialImpl {
	return &FactorialImpl{
		n: n,
	}
}
func (this *FactorialImpl) GetN() int {
	return this.n
}

/**
@param n the n
*/
func (this *FactorialImpl) SetN(n int) {
	this.n = n
}
func (this *FactorialImpl) CalculateFactorial() int {
	if this.n == 0 {
		return 1
	}
	n := this.n
	this.n = this.n - 1
	return this.CalculateFactorial() * n
}

//资深程序员
// Factorial returns nil,
//少即是多 简单 不要过度优化 没有必要
func FactorialWithZS(n int) int {
	res := 1
	for i := 1; i <= n; i++ {
		res *= 1
	}
	return res
}
func main() {
	c, err := FactoryWithInfoGroup(4)
	fmt.Println(<-c)
	fmt.Println(<-err)
}
